﻿using System.Collections.Generic;

namespace AIFSContentManagementSystem.Criteria
{
    public abstract class Expression
    {
        protected const string EQUAL_TO = "=";
        protected const string LESS_THAN = "<";
        protected const string GREATER_THAN = ">";
        protected const string LESS_THAN_EQUAL_TO = "<=";
        protected const string GREATER_THAN_EQUAL_TO = ">=";
        protected const string NULL_STRING = "NULL";

        ExpressionElement expressionElement;

        public ExpressionElement ExpressionElement
        {
            get { return expressionElement; }
            set { expressionElement = value; }
        }

        /// <summary>
        /// Restrict creating instances without CreateInstance call
        /// </summary>
        public Expression()
        {
            expressionElement = new ExpressionElement();
        }

        /// <summary>
        /// Implement this to do derieved class specific checking if necessary.
        /// </summary>
        protected virtual bool CanCompare(Expression other)
        {
            return true;
        }

        /// <summary>
        /// Implement this to provide the data type associated with it.
        /// </summary>
        public abstract ExpressionDataType DataType {get;}

        /// <summary>
        /// Implement this to actually perform the matching
        /// </summary>
        protected abstract int CompareTo(Expression other);

        public bool Match(Expression other)
        {
            //Make sure the expressions are comparable
            if (other == null)
                return false;

            if (string.IsNullOrEmpty(other.ExpressionElement.Oper) || string.IsNullOrEmpty(this.ExpressionElement.Oper))
                return false;

            if (string.IsNullOrEmpty(other.ExpressionElement.LeftOperand) || string.IsNullOrEmpty(this.ExpressionElement.LeftOperand))
                return false;

            if (string.IsNullOrEmpty(other.ExpressionElement.RightOperand) || string.IsNullOrEmpty(this.ExpressionElement.RightOperand))
                return false;

            if (!this.ExpressionElement.LeftOperand.Equals(other.ExpressionElement.LeftOperand))
                return false;

            //Check any specific rules for each expression types
            if (!this.CanCompare(other))
                return false;

            int compareResult = CompareTo(other);

            /*IF    THIS.OPER   other.OPER (from rule db)  CMP-RESULT MUST BE
             *      (from user) (from db)
             *      =           =           0
             *      =           <           -1
             *      =           >           1
             *      =           <=          0,-1
             *      =           >=          0,1
            */

            switch (other.ExpressionElement.Oper)
            {
                case EQUAL_TO:
                    if (compareResult == 0)
                        return true;
                    break;
                case LESS_THAN:
                    if (compareResult == -1)
                        return true;
                    break;
                case GREATER_THAN:
                    if (compareResult == 1)
                        return true;
                    break;
                case LESS_THAN_EQUAL_TO:
                    if (compareResult <= 0)
                        return true;
                    break;
                case GREATER_THAN_EQUAL_TO:
                    if (compareResult >= 0)
                        return true;
                    break;
            }

            return false;
        }

        /// <summary>
        /// Method creates an instance of CriterionExpression based on it's data type and the default criterion expression factory.
        /// Use the overload method if you need to provide a specific factory
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns>A derieved instance of CriterionExpression</returns>
        public static Expression CreateInstance(ExpressionDataType dataType)
        {
            return CreateInstance(dataType, new DefaultExpressionFactory());
        }

        /// <summary>
        /// Method creates an instance of CriterionExpression based on it's data type and the provided implementation of the criterion expression factory.
        /// </summary>
        /// <param name="dataType"></param>
        /// <param name="criterionExpressionFactory"></param>
        /// <returns></returns>
        public static Expression CreateInstance(ExpressionDataType dataType, IExpressionFactory criterionExpressionFactory)
        {
            return criterionExpressionFactory.CreateInstance(dataType);
        }

        /// <summary>
        /// Method creates an instance of CriterionExpression based on it's data type and the default criterion expression factory.
        /// Use the overload method if you need to provide a specific factory
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns>A derieved instance of CriterionExpression</returns>
        public static Expression CreateInstance(string dataType)
        {
            return CreateInstance(dataType, new DefaultExpressionFactory());
        }

        /// <summary>
        /// Method creates an instance of CriterionExpression based on it's data type and the default criterion expression factory.
        /// Use the overload method if you need to provide a specific factory
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns>A derieved instance of CriterionExpression</returns>
        public static List<Expression> CreateFromArray(string[] StringExpression)
        {
            return CreateFromArray(StringExpression, new DefaultExpressionFactory());
        }

        /// <summary>
        /// Method creates an instance of CriterionExpression based on it's data type and the default criterion expression factory.
        /// Use the overload method if you need to provide a specific factory
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns>A derieved instance of CriterionExpression</returns>
        public static List<Expression> CreateFromArray(string[] StringExpression, IExpressionFactory ExpressionFactory)
        {
            return ExpressionFactory.CreateFromArray(StringExpression);
        }

        /// <summary>
        /// Method creates an instance of CriterionExpression based on it's data type and the provided implementation of the criterion expression factory.
        /// </summary>
        /// <param name="dataType"></param>
        /// <param name="criterionExpressionFactory"></param>
        /// <returns></returns>
        public static Expression CreateInstance(string dataType, IExpressionFactory criterionExpressionFactory)
        {
            return criterionExpressionFactory.CreateInstance(dataType);
        }

        public override string ToString()
        {
            return string.Format("{0} {1} {2} {3}",
                this.DataType,
                this.ExpressionElement.LeftOperand,
                this.ExpressionElement.Oper,
                this.ExpressionElement.RightOperand);
        }
    }
}
